/**
 * Copyright (C) 2013 Denatics Development
 *
 * Programmed by David Tiersch <dtiersch@denatics.com>
 *
 * This file is part of B0mberman.
 *
 * B0mberman is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * B0mberman is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with B0mberman. If not, see <http://www.gnu.org/licenses/>.
 */
package com.denatics.bomberman.engine;

import com.denatics.bomberman.Config;
import com.denatics.bomberman.entities.Block;
import com.denatics.bomberman.entities.Wall;
import com.denatics.bomberman.sprites.Sprite;

/**
 * The Gamefield to manage everything on it.
 *
 * @author David Tiersch <dtiersch@denatics.com>
 */
public final class Field {

    /**
     * Size of the border (on both sides).
     */
    private static final int BORDERSIZE = 2;

    /**
     * Amount of Tiles in the Width.
     */
    private final int maxX;

    /**
     * Amount of Tiles in the Height.
     */
    private final int maxY;

    /**
     * Array of the tiles.
     */
    private final Tile[][] tiles;

    /**
     * Creates the Gamefield with the given dimension.
     *
     * @param width
     *            width of the field
     * @param height
     *            height of the field
     */
    public Field(final int width, final int height) {
        this.maxX = width;
        this.maxY = height;
        // Containing a set of Walls as border.
        this.tiles = new Tile[this.maxX + BORDERSIZE][this.maxY + BORDERSIZE];
        for (int i = 0; i < this.maxX + BORDERSIZE; i++) {
            for (int j = 0; j < this.maxY + BORDERSIZE; j++) {
                if (i % (this.maxX + 1) == 0 || j % (this.maxY + 1) == 0) {
                    // Border of the Game
                    this.tiles[i][j] = new Tile(new Wall());
                } else if (i % 2 == 0 && j % 2 == 0) {
                    // Fixed Blocks inside the Gamefield
                    this.tiles[i][j] = new Tile(new Wall());
                } else if (i % (this.maxX - 1) == 1
                        && j % (this.maxY - 1) == 1) {
                    // Empty Fields for the Players
                    this.tiles[i][j] = new Tile();
                } else if (i > this.maxX + BORDERSIZE) {
                    // TODO Some Random annoying Blocks
                    this.tiles[i][j] = new Tile(new Block());
                } else {
                    // everything else clean
                    this.tiles[i][j] = new Tile();
                }
            }
        }
    }

    /**
     * Returns the tile at the coordinates (x,y).
     *
     * @param x
     *            x-coordinate (border beginning at -1)
     * @param y
     *            y-coordinate (border beginning at -1)
     * @return Tile
     */
    public Tile getTile(final int x, final int y) {
        return this.tiles[x + 1][y + 1];
    }

    /**
     * Shows the coordinates, on which the Sprite is.
     *
     * @param sprite
     *            sprite to be searched for\
     *
     * @return coordinates
     */
    public int[] getCoords(final Sprite sprite) {
        for (int i = 0; i < this.maxX + BORDERSIZE; i++) {
            for (int j = 0; j < this.maxY + BORDERSIZE; j++) {
                if (this.tiles[i][j].getSprites().contains(sprite)) {
                    final int[] coords = { i - 1, j - 1 };
                    return coords;
                }
            }
        }
        return null;
    }

    /**
     * Checks if the new position is valid or collides with solid objects.
     *
     * @param sprite
     *            Sprite to be moved
     * @param offset
     *            new offset of the sprite
     *
     * @return if the move is being blocked
     */
    public boolean intersects(final Sprite sprite, final int[] offset) {
        final int[] coords = this.getCoords(sprite);
        final int[] realcoordsul =
        {
                coords[0] * Config.ENTITYSIZE + this.realOffset(offset[0])
                        + sprite.getPadding(),
                coords[1] * Config.ENTITYSIZE + this.realOffset(offset[1])
                        + sprite.getPadding(),
        };
        final int[] realcoordslr =
        {
                coords[0] * Config.ENTITYSIZE + this.realOffset(offset[0])
                        + Config.SPRITESIZE - sprite.getPadding() - 1,
                coords[1] * Config.ENTITYSIZE + this.realOffset(offset[1])
                        + Config.SPRITESIZE - sprite.getPadding() - 1,
        };
        // check entities
        if (this.getTile(realcoordsul[0] / Config.ENTITYSIZE,
                realcoordsul[1] / Config.ENTITYSIZE).getEntity().isSolid()
                || this.getTile(realcoordslr[0] / Config.ENTITYSIZE,
                        realcoordsul[1] / Config.ENTITYSIZE).getEntity()
                        .isSolid()
                || this.getTile(realcoordsul[0] / Config.ENTITYSIZE,
                        realcoordslr[1] / Config.ENTITYSIZE).getEntity()
                        .isSolid()
                || this.getTile(realcoordslr[0] / Config.ENTITYSIZE,
                        realcoordslr[1] / Config.ENTITYSIZE).getEntity()
                        .isSolid()
                || realcoordsul[0] < 0
                || realcoordsul[1] < 0) {
            return true;
        }

        // TODO Check Sprites

        return false;
    }

    /**
     * calculates the coordinates out of the offset which depends on the
     * Movement speed.
     *
     * @param offset
     *            Offset
     *
     * @return correspondending coordinate
     */
    private int realOffset(final int offset) {
        return (offset * Config.ENTITYSIZE) / Config.MOVEMENT_SPEED;
    }
}
